package cn.jofei.server.http.filter;

import cn.jofei.common.ano.Component;
import cn.jofei.common.ano.Order;
import cn.jofei.common.ano.Value;
import cn.jofei.common.config.AppProperties;
import cn.jofei.common.enums.Headers;
import cn.jofei.common.enums.MediaType;
import cn.jofei.common.reflect.ReflectionMethod;
import cn.jofei.common.utils.Utils;
import cn.jofei.core.container.Container;
import cn.jofei.server.http.ExceptionHandler;
import cn.jofei.server.http.impl.DefaultExceptionHandler;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Parameter;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 系统内置请求过滤器
 *
 * @author Jofei Bao
 * @version ngbao v1.0.2
 * @date 2022年07月08日 23:19
 */
@Order(index = Integer.MIN_VALUE)
@Component
public class SysRequestFilter implements HttpRequestFilter {

    public static final Logger log = LoggerFactory.getLogger(SysRequestFilter.class);
    public static final List<String> WHITE_LIST = AppProperties.get("server.request.white-list",List.class).orElse(new ArrayList<String>());

    @Value(name = "${server.listen.prefix}")
    private String prefix;

    private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();

    @Override
    public boolean doFilter(HttpRequest request, ChannelHandlerContext ctx) throws Exception {
        String uri = request.uri();
        if (uri.contains("//")) {
            String url = uri.substring(uri.indexOf("//") + 2);
            uri = url.substring(url.indexOf("/"));
        }
        if (uri.startsWith(prefix)) {

            if (uri.contains("?")) {
                uri = uri.substring(0, uri.indexOf("?"));
            }
            Object result;
            try {
                ReflectionMethod method = Container.SERVER_LET_MAP.get(uri);
                if (Utils.nonNull(method)) {
                    ctx.channel().config().setAutoRead(true);
                    JSONObject params = getParams(request.uri());
                    if (request instanceof FullHttpRequest) {
                        String json = ((FullHttpRequest) request).content().toString(StandardCharsets.UTF_8);
                        if (Utils.nonNull(json) && json.contains("{")) {
                            params.putAll(JSON.parseObject(json));
                        }
                    }

                    result = method.execution(getMethodArgs(request, ctx, params, method.getMethod().getParameters()));

                    if (result == null) {
                        return true;
                    }
                } else {
                    throw new RuntimeException("请求路径：" + uri + " 方法未实现");
                }
            } catch (Exception e) {
                result = exceptionHandler.handle(e);
            }
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.OK);
            if (result instanceof HttpResponse) {
                HttpResponse entity = (HttpResponse) result;
                ctx.writeAndFlush(entity);
                ctx.close();
                return true;
            } else if (result instanceof byte[]) {
                response.content().writeBytes((byte[]) result);
            } else if (result instanceof String) {
                String bytes = (String) result;
                response.headers().add(Headers.CONTENT_TYPE, MediaType.HTML);
                response.content().writeCharSequence(bytes, StandardCharsets.UTF_8);
            } else {
                response.headers().add(Headers.CONTENT_TYPE, MediaType.JSON);
                response.content().writeCharSequence(JSON.toJSONString(result), StandardCharsets.UTF_8);
            }
            ctx.writeAndFlush(response);
            ctx.close();
            return true;
        }
        return false;
    }

    /**
     * 获取方法入参
     *
     * @param request
     * @param ctx
     * @param params
     * @param parameters
     * @return
     */
    private Object[] getMethodArgs(HttpRequest request, ChannelHandlerContext ctx, JSONObject params, Parameter[] parameters) {
        return Arrays.stream(parameters).map(parameter -> {
            Class<?> clazz = parameter.getType();
            try {
                if (Map.class.isAssignableFrom(clazz)) {
                    return params;
                } else if (HttpRequest.class.isAssignableFrom(clazz)) {
                    return request;
                } else if (ChannelHandlerContext.class.isAssignableFrom(clazz)) {
                    return ctx;
                } else {
                    if (Utils.isMinType(clazz)) {
                        return params.getObject(parameter.getName(), clazz);
                    } else {
                        return JSON.parseObject(JSON.toJSONString(params), clazz);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).toArray();
    }

    /**
     * 获取uri参数为JSON
     *
     * @param uri
     * @return
     */
    private JSONObject getParams(String uri) {
        JSONObject params = new JSONObject();
        if (uri.contains("?")) {
            String end = uri.substring(uri.indexOf("?") + 1);
            if (end.contains("=")) {
                Arrays.stream(end.split("&")).forEach(e -> {
                    String[] vars = e.split("=");
                    params.put(vars[0], vars[1]);
                });
            }
        }
        return params;
    }
}
